package com.shadow.demo.shiro.shiro;

import com.shadow.demo.common.constants.PunctuationConstants;
import com.shadow.demo.shiro.shiro.filter.SecurityFilter;
import com.shadow.demo.shiro.shiro.filter.XssFilter;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

/**
 *
 */
@Configuration
@ConditionalOnWebApplication
public class ShiroConfig {

    /** 首页. **/
    private static final String INDEX        = "/";
    /** 登录地址. **/
    private static final String LOGIN        = "/login";
    /** 未授权界面. **/
    private static final String UNAUTHORIZED = "/403";
    /** 匿名资源的标记类型. **/
    private static final String ANON         = "anon";
    /** 认证资源的标记类型. **/
    private static final String SECURITY     = "security";
    /** XSS安全标记类型. **/
    private static final String XSS          = "xss";
    /** filter拦截地址设置匹配所有. **/
    private static final String ALL          = "/**";
    /** 配置匿名请求地址. **/
    private static final String [] WHITE     = {
        "/favicon.png",
            "/css/**",
            "/js/**",
            "/layui/**",
            "/res/**",
            LOGIN,
            "/public/**",
            "/**/public/**",
    };
    /** xss开关. **/
    @Value("${xss.enabled}")
    private Boolean enabled;

    /**
     * 将自定义的验证方式加入shiro管理器.
     *
     * @return {@link DefaultWebSecurityManager}
     **/
    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getShiroDbRealm());
        return securityManager;
    }

    /**
     * 实例化自定义认证.
     *
     * @return {@link ShiroDbRealm}
     **/
    @Bean
    public ShiroDbRealm getShiroDbRealm() {
        return new ShiroDbRealm();
    }

    /**
     * Filter工厂配置，设置请求对应的过滤条件和跳转条件.
     * 检验顺序: 白名单 -> SecurityFilter -> XssFilter -> OtherFilter -> /**
     *
     * @param securityManager 安全认证管理器
     * @return {@link ShiroFilterFactoryBean}
     **/
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(final DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSuccessUrl(INDEX);
        factoryBean.setLoginUrl(LOGIN);
        factoryBean.setUnauthorizedUrl(UNAUTHORIZED);
        factoryBean.setFilters(getFilterMap());
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setFilterChainDefinitionMap(getFilterRuleMap());
        return factoryBean;
    }

    /**
     * 拦截器过滤链.
     *
     * @return {@link HashMap}
     **/
    private Map<String, Filter> getFilterMap() {
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put(SECURITY, new SecurityFilter());
        Optional.ofNullable(enabled)
                .filter(BooleanUtils::isTrue)
                .ifPresent(x -> filterMap.put(XSS, new XssFilter()));
        return filterMap;
    }

    /**
     * Filter过滤规则.
     * 1、匿名地址配置
     * 2、filter各自拦截url配置,从上向下顺序执行
     *
     * @return {@link HashMap}
     */
    private Map<String, String> getFilterRuleMap() {
        Map<String, String> filterRuleMap = new HashMap<>();
        Arrays.asList(WHITE).forEach(x -> filterRuleMap.put(x, ANON));

        final List<String> filterList  = new ArrayList<>();
        filterList.add(SECURITY);
        Optional.ofNullable(enabled)
                .filter(BooleanUtils::isTrue)
                .ifPresent(x -> filterList.add(XSS));
        filterRuleMap.put(ALL, String.join(PunctuationConstants.SPLIT_COMMA, filterList));
        return filterRuleMap;
    }


    /**
     * 强制使用cglib，防止重复代理和可能引起代理出错的问题.
     *
     * @return {@link DefaultAdvisorAutoProxyCreator}
     **/
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * lifecycleBeanPostProcessor.
     *
     * @return {@link LifecycleBeanPostProcessor}
     **/
    @Bean
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * authorizationAttributeSourceAdvisor.
     *
     * @param securityManager securityManager
     * @return {@link AuthorizationAttributeSourceAdvisor}
     **/
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(final DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}